"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.installComponent = exports.getUserAndroidPath = exports.getBestHypervisor = exports.getAndroidSdkRootInstallation = exports.enableWHPX = exports.enableHAXM = exports.enableAMDH = exports.createAVD = void 0;
function _fsExtra() {
  const data = require("fs-extra");
  _fsExtra = function () {
    return data;
  };
  return data;
}
function _path() {
  const data = require("path");
  _path = function () {
    return data;
  };
  return data;
}
var _executeWinCommand = require("./executeWinCommand");
var _processorType = require("./processorType");
/**
 * Returns the path to where all Android related things should be installed
 * locally to the user.
 */
const getUserAndroidPath = () => {
  return (0, _path().join)(process.env.LOCALAPPDATA || '', 'Android');
};

/**
 * Deals with ANDROID_HOME, ANDROID_SDK_ROOT or generates a new one
 */
exports.getUserAndroidPath = getUserAndroidPath;
const getAndroidSdkRootInstallation = () => {
  const env = process.env.ANDROID_SDK_ROOT || process.env.ANDROID_HOME;
  const installPath = env ?
  // Happens if previous installations or not fully completed
  env :
  // All Android zip files have a root folder, using `Android` as the common place
  (0, _path().join)(getUserAndroidPath(), 'Sdk');
  if ((0, _fsExtra().pathExistsSync)(installPath)) {
    return installPath;
  } else {
    return '';
  }
};

/**
 * Installs an Android component (e.g.: `platform-tools`, `emulator`)
 * using the `sdkmanager` tool and automatically accepting the licenses.
 */
exports.getAndroidSdkRootInstallation = getAndroidSdkRootInstallation;
const installComponent = (component, androidSdkRoot) => {
  return new Promise((done, error) => {
    var _child$stdout, _child$stderr;
    const sdkmanager = (0, _path().join)(androidSdkRoot, 'tools', 'bin', 'sdkmanager.bat');
    const command = `"${sdkmanager}" --sdk_root="${androidSdkRoot}" "${component}"`;
    const child = (0, _executeWinCommand.executeCommand)(command);
    let stderr = '';
    (_child$stdout = child.stdout) === null || _child$stdout === void 0 ? void 0 : _child$stdout.on('data', data => {
      if (data.includes('(y/N)')) {
        var _child$stdin;
        (_child$stdin = child.stdin) === null || _child$stdin === void 0 ? void 0 : _child$stdin.write('y\n');
      }
    });
    (_child$stderr = child.stderr) === null || _child$stderr === void 0 ? void 0 : _child$stderr.on('data', data => {
      stderr += data.toString('utf-8');
    });
    child.on('close', exitStatus => {
      if (exitStatus === 0) {
        done(undefined);
      } else {
        error({
          stderr
        });
      }
    });
    child.on('error', error);
  });
};

/**
 * For the given custom Hypervisor and the output of `emulator-check accel`
 * returns the preferred Hypervisor to use and its installation status.
 * The recommendation order is:
 * 1. WHPX
 * 2. HAXM if Intel
 * 3. AMDH if AMD
 */
exports.installComponent = installComponent;
const parseHypervisor = (status, customHypervisor) => {
  /**
   * Messages:
   * Android Emulator requires an Intel processor with VT-x and NX support.  Your CPU: 'AuthenticAMD'
   * HAXM is not installed, but Windows Hypervisor Platform is available.
   * WHPX (10.0.19041) is installed and usable.
   * * This message outputs for WHPX and when the AMD Hypervisor is installed
   * HAXM version 6.2.1 (4) is installed and usable.
   * HAXM is not installed on this machine
   */

  if (status.includes('is not installed, but Windows Hypervisor Platform is available.')) {
    return {
      hypervisor: 'WHPX',
      installed: false
    };
  }
  if (/WHPX \((\d|\.)+\) is installed and usable\./.test(status)) {
    return {
      hypervisor: 'WHPX',
      installed: true
    };
  }
  if (/is installed and usable\./.test(status)) {
    return {
      hypervisor: customHypervisor,
      installed: true
    };
  }
  if (status.includes("Your CPU: 'AuthenticAMD'")) {
    return {
      hypervisor: customHypervisor,
      installed: false
    };
  }
  if (status.includes('is not installed on this machine')) {
    return {
      hypervisor: 'none',
      installed: false
    };
  }
  return null;
};
const getEmulatorAccelOutputInformation = async androidSDKRoot => {
  /**
   * The output of the following command is something like:
   *
   * ```
   * accel:
   * 0
   * WHPX (10.0.19041) is installed and usable.
   * accel
   * ```
   *
   * If it fails it will still output to stdout with a similar format:
   *
   * ```
   * accel:
   * 1
   * Android Emulator does not support nested virtualization.  Your VM host: 'Microsoft Hv' (Hyper-V)
   * accel
   * ```
   *
   */

  try {
    const {
      stdout
    } = await (0, _executeWinCommand.executeCommand)(`"${(0, _path().join)(androidSDKRoot, 'emulator', 'emulator-check.exe')}" accel`);
    return stdout;
  } catch (e) {
    const {
      stdout
    } = e;
    return stdout;
  }
};

/**
 * Creates a new Android Virtual Device in the default folder with the
 * name, device and system image passed by parameter.
 */
const createAVD = async (androidSDKRoot, name, device, image) => {
  try {
    const abi = image.includes('x86_64') ? 'x86_64' : 'x86';
    const tag = image.includes('google_apis') ? 'google_apis' : 'generic';
    const avdmanager = (0, _path().join)(androidSDKRoot, 'tools', 'bin', 'avdmanager.bat');
    const {
      stdout
    } = await (0, _executeWinCommand.executeCommand)(`${avdmanager} -s create avd --force --name "${name}" --device "${device}" --package "${image}" --tag "${tag}" --abi "${abi}"`);

    // For some reason `image.sysdir.1` in `config.ini` points to the wrong location and needs to be updated
    const configPath = (0, _path().join)(process.env.HOMEPATH || '', '.android', 'avd', `${name}.avd`, 'config.ini');
    const content = await (0, _fsExtra().readFile)(configPath, 'utf-8');
    const updatedContent = content.replace(/Sdk\\system-images/g, 'system-images');
    await (0, _fsExtra().writeFile)(configPath, updatedContent, 'utf-8');
    return stdout;
  } catch (e) {
    const {
      stderr
    } = e;
    return stderr;
  }
};

/**
 * Returns what hypervisor should be installed for the Android emulator
 * using [Microsoft's official
 * documentation](https://docs.microsoft.com/en-us/xamarin/android/get-started/installation/android-emulator/hardware-acceleration?pivots=windows)
 * as a reference.
 */
exports.createAVD = createAVD;
const getBestHypervisor = async androidSDKRoot => {
  const customHypervisor = (0, _processorType.getProcessorType)() === 'Intel' ? 'HAXM' : 'AMDH';
  const stdout = await getEmulatorAccelOutputInformation(androidSDKRoot);
  const lines = stdout.split('\n');
  for (const line of lines) {
    const hypervisor = parseHypervisor(line, customHypervisor);
    if (hypervisor) {
      return hypervisor;
    }
  }

  // Couldn't identify the best one to run so not doing anything
  return {
    hypervisor: 'none',
    installed: false
  };
};

/**
 * Enables the Windows HypervisorPlatform and Hyper-V features.
 * Will prompt the User Account Control (UAC)
 */
exports.getBestHypervisor = getBestHypervisor;
const enableWHPX = () => {
  return (0, _executeWinCommand.executeCommand)('DISM /Quiet /NoRestart /Online /Enable-Feature /All /FeatureName:Microsoft-Hyper-V /FeatureName:HypervisorPlatform', true);
};

/**
 * Installs and enables the [HAXM](https://github.com/intel/haxm)
 * version available through the Android SDK manager.
 * @param androidSdkInstallPath The path to the Android SDK installation
 */
exports.enableWHPX = enableWHPX;
const enableHAXM = async androidSdkInstallPath => {
  await installComponent('extras;intel;Hardware_Accelerated_Execution_Manager', androidSdkInstallPath);
  await (0, _executeWinCommand.executeCommand)((0, _path().join)(androidSdkInstallPath, 'Sdk', 'extras', 'intel', 'Hardware_Accelerated_Execution_Manager', 'silent_install.bat'));
};

/**
 * Installs and enables the
 * [Hypervisor Driver for AMD Processors](https://androidstudio.googleblog.com/2019/10/android-emulator-hypervisor-driver-for.html)
 * version available through the Android SDK manager.
 * @param androidSdkInstallPath The path to the Android SDK installation
 */
exports.enableHAXM = enableHAXM;
const enableAMDH = async androidSdkInstallPath => {
  await installComponent('extras;google;Android_Emulator_Hypervisor_Driver', androidSdkInstallPath);
  await (0, _executeWinCommand.executeCommand)((0, _path().join)(androidSdkInstallPath, 'Sdk', 'extras', 'google', 'Android_Emulator_Hypervisor_Driver', 'silent_install.bat'));
};
exports.enableAMDH = enableAMDH;

//# sourceMappingURL=androidWinHelpers.ts.map